﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Collections;

namespace TUtils.BasicHelper
{
	/// <summary>
	/// 
	/// </summary>
	/// <remarks>
	/// <example>
	/// make a class comparable, which isn't derived from another class
	/// <code><![CDATA[
	/// public class Test78275346 : EqualityImplementor<Test78275346>
	/// {
	/// 	private int a;
	/// 
	/// 	#region equality implementation
	/// 	/// <summary>
	/// 	/// don't call this method directly. Use "==" or Equals(..) instead.
	/// 	/// </summary>
	/// 	public override bool IsEqualInternal(Test78275346 obj1, Test78275346 obj2)
	/// 	{
	/// 		return obj1.a == obj2.a;
	/// 	}
	/// 
	/// 	public override int GetHashCodeInternal(Test78275346 obj)
	/// 	{
	/// 		return obj.a;
	/// 	}
	/// 
	/// 	public static bool operator ==(Test78275346 x, Test78275346 y)
	/// 	{
	/// 		return EqualsStatic(x, y);
	/// 	}
	/// 
	/// 	public static bool operator !=(Test78275346 x, Test78275346 y)
	/// 	{
	/// 		return !(x == y);
	/// 	}
	/// 	#endregion
	/// }
	/// ]]></code>
	/// </example>
	/// 
	/// <example>
	/// Make a class comparable, which is derived from another class
	/// <code><![CDATA[
	/// public class Test7827864 : IEqualityImplementor<Test7827864>
	/// {
	/// 	private int a;
	/// 
	/// 	#region equality implementation
	/// 	private class EqualityImplementation : EqualityImplementor<Test7827864>
	/// 	{
	/// 		public EqualityImplementation(Test7827864 thisObj)
	/// 			: base(thisObj)
	/// 		{
	/// 		}
	/// 
	/// 		public override bool IsEqualInternal(Test7827864 obj1, Test7827864 obj2)
	/// 		{
	/// 			return obj1.a == obj2.a;
	/// 		}
	/// 
	/// 		public override int GetHashCodeInternal(Test7827864 obj)
	/// 		{
	/// 			return obj.a;
	/// 		}
	/// 	}
	/// 
	/// 	private EqualityImplementation equalityImplementor;
	/// 	private EqualityImplementation EqualityImplementor
	/// 	{
	/// 		get
	/// 		{
	/// 			if ( equalityImplementor == null )
	/// 			{
	/// 				equalityImplementor = new EqualityImplementation(this);
	/// 			}
	/// 
	/// 			return equalityImplementor;
	/// 		}
	/// 	}
	/// 
	/// 	public static bool operator ==(Test7827864 x, Test7827864 y)
	/// 	{
	/// 		return EqualityImplementation.EqualsStatic(x,y);
	/// 	}
	/// 
	/// 	public static bool operator !=(Test7827864 x, Test7827864 y)
	/// 	{
	/// 		return !(x == y);
	/// 	}
	/// 
	/// 	public override int GetHashCode()
	/// 	{
	/// 		return EqualityImplementor.GetHashCode();
	/// 	}
	/// 
	/// 	public int GetHashCode(object obj)
	/// 	{
	/// 		return EqualityImplementor.GetHashCode(obj);
	/// 	}
	/// 
	/// 	public int GetHashCode(Test7827864 obj)
	/// 	{
	/// 		return EqualityImplementor.GetHashCode(obj);
	/// 	}
	/// 
	/// 	public override bool Equals(object obj)
	/// 	{
	/// 		return EqualityImplementor.Equals(obj);
	/// 	}
	/// 
	/// 	bool IEqualityComparer.Equals(object x, object y)
	/// 	{
	/// 		return (EqualityImplementor as IEqualityComparer).Equals(x, y);
	/// 	}
	/// 
	/// 	public bool Equals(Test7827864 x, Test7827864 y)
	/// 	{
	/// 		return EqualityImplementor.Equals(x, y);
	/// 	}
	/// 
	/// 	public bool Equals(Test7827864 other)
	/// 	{
	/// 		return EqualityImplementor.Equals(other);
	/// 	}
	/// 	#endregion
	/// }
	/// ]]></code>
	/// </example>
	/// </remarks>
	/// <typeparam name="T"></typeparam>
	public abstract class EqualityImplementor<T> : IEqualityImplementor<T>
		where T : class, IEqualityImplementor<T>
	{
		private T __obj;

		protected EqualityImplementor()
		{
			__obj = this as T;
		}

		public EqualityImplementor(T obj)
		{
			__obj = obj;
		}

		private bool ObjectsAreEqual(object x, object y)
		{
			if (x == null)
				return (y == null);
			else if (y == null)
				return false;

			var X = x as T;
			if (X == null)
				return false;
			var Y = y as T;
			if (Y == null)
				return false;

			return IsEqualInternal(X,Y);
		}

		bool IEqualityComparer.Equals(object x, object y)
		{
			return ObjectsAreEqual(x, y);
		}

		public override int GetHashCode()
		{
			return GetHashCode((object)__obj);
		}

		public int GetHashCode(object obj)
		{
            if (obj == null)
                return 0;

		    if (!(obj is T))
		        throw new ApplicationException("67asfjsklcb7w3jrmbnui " + obj.GetType());

		    return this.GetHashCodeInternal(obj as T);
		}

		public bool Equals(T x, T y)
		{
			return ObjectsAreEqual(x, y);
		}

		public int GetHashCode(T obj)
		{
			return GetHashCode((object)obj);
		}

		public static bool EqualsStatic(T x, T y)
		{
			if (x == null)
				return (y == null);
			else if (y == null)
				return false;

			return x.Equals(y);
		}


		public bool Equals(T other)
		{
			return ObjectsAreEqual(__obj, other);
		}

		public override bool Equals(object obj)
		{
			return ObjectsAreEqual(__obj, obj);
		}

		/// <summary>
		/// returns true, if obj1 is equal obj2.
		/// obj1 and obj2 are not null.
		/// Don't call "==" or Equals(..) within this method to avoid endless loops.
		/// </summary>
		/// <param name="obj1"></param>
		/// <param name="obj2"></param>
		/// <returns></returns>
		public abstract bool IsEqualInternal(T obj1, T obj2);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public abstract int GetHashCodeInternal(T obj);
	}

	public interface IEqualityImplementor<T> : IEqualityComparer, IEqualityComparer<T>, IEquatable<T>
		where T : class
	{
	}

}
